﻿using AngleSharp;
using WebGrease.Css.Extensions;

namespace HynuIOTA.Web.Filters
{
    using HynuIOTA.Entity.FormatEntity;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;

    public class PermissionValidateAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// 权限过滤上下文
        /// </summary>
        protected AuthorizationContext FiterContext { get; set; }

        /// <summary>
        /// 是区域请求
        /// </summary>
        protected bool IsAreaRequest { get; set; }

        /// <summary>
        /// 请求区域名
        /// </summary>
        protected string AreaName { get; set; } = string.Empty;

        /// <summary>
        ///  请求Action方法名
        /// </summary>
        protected string ActionName { get; set; } = string.Empty;

        /// <summary>
        /// 请求控制器名
        /// </summary>
        protected string ControllerName { get; set; } = string.Empty;

        /// <summary>
        /// 请求方法名
        /// </summary>
        protected string Method { get; set; }

        /// <summary>
        /// 操作上下文
        /// </summary>
        public OperationContext OpContext => OperationContext.Current;

        /// <summary>
        /// 初始化属性
        /// </summary>
        /// <param name="filterContext"></param>
        private void InitProperty(AuthorizationContext filterContext)
        {
            this.FiterContext = filterContext;

            if (FiterContext.ActionDescriptor.ActionName != null)
                ActionName = FiterContext.ActionDescriptor.ActionName.ToLower();

            if (FiterContext.ActionDescriptor.ControllerDescriptor.ControllerName != null)
                ControllerName = FiterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();

            if (FiterContext.HttpContext.Request.HttpMethod != null)
                Method = FiterContext.HttpContext.Request.HttpMethod.ToLower();
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            // 设置SetRequestContext
            UrlConst.SetRequestContext(filterContext.RequestContext);

            // 如果不存在区域，那么直接放行
            if (filterContext.RouteData.DataTokens.ContainsKey("area") == false)
            {
                return;
            }

            // 如果登录用户为超级管理员，则跳过权限认证
            if (IsLogin() && OpContext.UserEntity.UserName == "admin")
            {
                return;
            }

            // 初始化属性
            InitProperty(filterContext);

            // 如果贴上跳过登录标签，则返回
            if (DoseStickAttr<Common.Attribute.SkipLoginAttribute>(filterContext.ActionDescriptor))
            {
                return;
            }

            // 获取请求URL
            string requestUrl = GetRequestUrl(filterContext);

            if (IsLogin() == false)
            {
                // 是否Remember User
                _ = OperationContext.CheckRememberUser(OpContext).Result;
            }

            // 如果未登录，判断该url是否为未登录权限所有
            if (IsLogin() == false)
            {
                // 查询未登录用户中是否有结果
                var result = OpContext.Service.Relo.WhereFristOrDefault(r => r.Name == "未登录")?.Action.Where(a => a.Url == requestUrl);

                if (result != null && result.Any())
                {
                    return;
                }

                ProcessResult(filterContext, Entity.FormatEntity.AjaxMsgStatus.NoLogin, "对不起，您没有登录!", UrlConst.Url.Action("Login", "User", new { area = "Users", @goto = OpContext.Request.RawUrl }));
                return;
            }

            // 如果标记跳过权限认证标签，则返回true
            if (DoseStickAttr<Common.Attribute.SkipPermissionAttribute>(filterContext.ActionDescriptor))
            {
                return;
            }

            // 获取用户的权限列表
            var actions = GetAction(OpContext.UserEntity);
            // 判断是否有权限
            if (!HsPermission(actions))
            {
                ProcessResult(filterContext, Entity.FormatEntity.AjaxMsgStatus.NoPer, "对不起，您没有当前操作权限!");
            }
        }

        private void ProcessResult(AuthorizationContext filterContext, AjaxMsgStatus status = AjaxMsgStatus.OK, string msg = "", string backurl = "", object data = null)
        {
            // 如果类或方法贴上了AjaxRequest标签特性，那么代表浏览器Ajax请求，返回Json字符串
            if (DoseStickAttr<Common.Attribute.AjaxRequestAttribute>(filterContext.ActionDescriptor))
            {
                filterContext.Result = OpContext.JsonMessage(status, msg, backurl, data);
            }
            else
            {
                if (string.IsNullOrEmpty(backurl) == true)
                {
                    filterContext.Result = OpContext.JsBackMsg(msg);
                    return;
                }
                filterContext.Result = OpContext.JsMsg(msg, backurl);
            }
        }

        /// <summary>
        /// 获取请求的 URL
        /// </summary>
        /// <param name="filterContext"></param>
        /// <returns></returns>
        private string GetRequestUrl(AuthorizationContext filterContext)
        {
            // 拼接请求字符串
            string requestUrl = string.Empty;

            // 判断请求中是否存在区域
            if (filterContext.RouteData.DataTokens.ContainsKey("area"))
            {
                // 请求区域名称
                string strArea = (string)filterContext.RouteData.DataTokens["area"] ?? "";
                // 拼接
                requestUrl = $"/{strArea}/{ControllerName}/{ActionName}";
            }
            else
            {
                requestUrl = $"/{ControllerName}/{ActionName}";
            }

            return requestUrl;
        }


        /// <summary>
        /// 获取用户的Action列表
        /// </summary>
        /// <param name="users"></param>
        /// <returns></returns>
        private static IEnumerable<Entity.Action> GetAction(Entity.User users)
        {
            return from r in users.Relo
                   from a in r.Action
                   select a;
        }

        /// <summary>
        /// 判断是否有权限
        /// </summary>
        /// <returns></returns>
        private bool HsPermission(IEnumerable<Entity.Action> actions)
        {
            // 过滤函数表达式
            bool WhereLambda(Entity.Action a)
            {
                return a.rController == "*" || (a.rController == ControllerName && (a.rAction == "*" || (a.rAction == ActionName && (a.rMethod == "*" || (a.rMethod == Method)))));
            }

            // 构造 有域请求情况下的过滤
            bool HaveArea(Entity.Action a) => (a.rArea == "*") || (a.rArea == AreaName && WhereLambda(a));
            // 构造 无域请求下的过滤
            bool NoArea(Entity.Action a) => a.rArea == "*" || WhereLambda(a);

            // 根据请求类型，选择控制器过滤方式
            IEnumerable<Entity.Action> result = null;

            result = IsAreaRequest ? actions.Where(HaveArea) : actions.Where(NoArea);

            // 如果返回结果中有元素，那么代表有权限
            return result.Any();
        }

        /// <summary>
        /// 判断是否登录
        /// </summary>
        /// <returns></returns>
        private bool IsLogin()
        {
            return OpContext.UserEntity != null;
        }

        /// <summary>
        /// 判断 方法 或 类上 是否贴有指定标记
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        private bool DoseStickAttr<T>(ActionDescriptor action)
        {
            Type t = typeof(T);
            return action.IsDefined(t, false) || action.ControllerDescriptor.IsDefined(t, false);
        }
    }
}